﻿using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Windows.Media;
using System.Xml.Serialization;

namespace Red.Controls.Diagrams.Declarators
{
    [XmlType( "Nodes" )]
    public class NodeDeclaratorFile
    {
        [XmlElement( "Node" )]
        public List<NodeDeclarator> Nodes { get; set; } 
    }

    [XmlType( "Node" )]
    public class NodeDeclarator
    {
        #region Data

        private Color? _color;
        private NodeVisualType? _visualType;
        private string _title;
        private bool? _isContainer;

        #endregion Data

        #region C-tor

        public NodeDeclarator()
        {
            InputsInternal = new List<NodeInputDeclarator>();
            OutputsInternal = new List<NodeOutputDeclarator>();
            DropsInternal = new List<NodeDropDeclarator>();
        }

        #endregion C-tor

        #region Properties

        [DefaultValue(false)]
        [XmlAttribute("Abstract")]
        public bool IsAbstract { get; set; }

        [XmlAttribute]
        public string Class { get; set; }

        [XmlIgnore]
        public bool IsContainer
        {
            get
            {
                if ( _isContainer.HasValue )
                    return _isContainer.Value;

                if ( Base != null )
                    return Base.IsContainer;

                return false;
            }

            set { _isContainer = value; }
        }

        [DefaultValue( false )]
        [XmlAttribute( "Container" )]
        public bool IsContainerInternal
        {
            get
            {
                if ( _isContainer.HasValue )
                    return _isContainer.Value;

                return false;
            }
            set { _isContainer = value; }
        }

        [XmlIgnore]
        public NodeVisualType VisualType 
        {
            get
            {
                if ( _visualType.HasValue )
                    return _visualType.Value;

                if ( Base != null )
                    return Base.VisualType;
                
                return NodeVisualType.Undefined;                
            }
            set { _visualType = value; }
        }

        [XmlAttribute( "VisualType" )]        
        public NodeVisualType VisualTypeInternal
        {
            get 
            {
                if ( _visualType.HasValue )
                    return _visualType.Value;

                return NodeVisualType.Undefined; 
            }
            set { _visualType = value; } 
        }

        /// <summary>
        /// In MS internal class System.Xml.Serialization.XmlSerializationWriterCodeGen they generate code to serialize object.
        /// For each MemberName that will be serialized they generate call to MemberNameSpecialized to check if this member should be skipped.
        /// In this way we can skip empty collections (which cannot be achieved by DefaultValueAttribute).
        /// We can also add some additional checks.
        /// https://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlserializer.aspx?cs-lang=csharp#code-snippet-5
        /// </summary>
        [XmlIgnore]
        public bool VisualTypeInternalSpecified
        {
            get { return _visualType.HasValue && _visualType.Value != NodeVisualType.Undefined; }
        }

        [XmlIgnore]
        public Color Color 
        { 
            get 
            { 
                if ( _color.HasValue )
                    return _color.Value;

                if ( Base != null )
                    return Base.Color;

                return Colors.DeepPink;
            }
            set { _color = value; }
        }

        [XmlAttribute( "Color" )]
        public string ColorInternal
        {
            get { return _color.ToString(); }
            set { _color = ( Color )ColorConverter.ConvertFromString( value ); }
        }

        /// <summary>
        /// In MS internal class System.Xml.Serialization.XmlSerializationWriterCodeGen they generate code to serialize object.
        /// For each MemberName that will be serialized they generate call to MemberNameSpecialized to check if this member should be skipped.
        /// In this way we can skip empty collections (which cannot be achieved by DefaultValueAttribute).
        /// We can also add some additional checks.
        /// https://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlserializer.aspx?cs-lang=csharp#code-snippet-5
        /// </summary>
        [XmlIgnore]
        public bool ColorInternalSpecified
        {
            get { return _color.HasValue; }
        }

        [XmlIgnore]
        public string Title 
        {
            get
            {
                if ( _title != null )
                    return _title;

                if ( Base != null )
                    return Base.Title;

                return string.Empty;
            }

            set { _title = value; }
        }

        [XmlAttribute( "Title" )]
        public string TitleInternal
        {
            get { return _title; }
            set { _title = value; }
        }

        [XmlAttribute]
        public string BaseClass { get; set; }

        [XmlIgnore]
        public List<NodeInputDeclarator> Inputs
        {
            get
            {
                var thisInputs = InputsInternal.ToList();
                if ( Base != null )
                    thisInputs.InsertRange( 0, Base.Inputs );

                return thisInputs;
            }
        }

        [XmlIgnore]
        public List<NodeOutputDeclarator> Outputs
        {
            get
            {
                var thisOutputs = OutputsInternal.ToList();
                if ( Base != null )
                    thisOutputs.InsertRange( 0, Base.Outputs );

                return thisOutputs;
            }
        }

        [XmlIgnore]
        public List<NodeDropDeclarator> Drops
        {
            get
            {
                var thisDrops = DropsInternal.ToList();
                if ( Base != null )
                    thisDrops.InsertRange( 0, Base.Drops );

                return thisDrops;
            }
        }

        [XmlArray( "Inputs" )]
        [DefaultValue(null)]
        public List<NodeInputDeclarator> InputsInternal { get; set; }

        /// <summary>
        /// In MS internal class System.Xml.Serialization.XmlSerializationWriterCodeGen they generate code to serialize object.
        /// For each MemberName that will be serialized they generate call to MemberNameSpecialized to check if this member should be skipped.
        /// In this way we can skip empty collections (which cannot be achieved by DefaultValueAttribute).
        /// We can also add some additional checks.
        /// https://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlserializer.aspx?cs-lang=csharp#code-snippet-5
        /// </summary>
        [XmlIgnore]
        public bool InputsInternalSpecified
        {
            get { return InputsInternal.Count > 0; }
        }

        [XmlArray( "Outputs" )]
        public List<NodeOutputDeclarator> OutputsInternal { get; set; }

        /// <summary>
        /// In MS internal class System.Xml.Serialization.XmlSerializationWriterCodeGen they generate code to serialize object.
        /// For each MemberName that will be serialized they generate call to MemberNameSpecialized to check if this member should be skipped.
        /// In this way we can skip empty collections (which cannot be achieved by DefaultValueAttribute).
        /// We can also add some additional checks.
        /// https://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlserializer.aspx?cs-lang=csharp#code-snippet-5
        /// </summary>
        [XmlIgnore]
        public bool OutputsInternalSpecified
        {
            get { return OutputsInternal.Count > 0; }
        }

        [XmlArray( "Drops" )]
        public List<NodeDropDeclarator> DropsInternal { get; set; }

        /// <summary>
        /// In MS internal class System.Xml.Serialization.XmlSerializationWriterCodeGen they generate code to serialize object.
        /// For each MemberName that will be serialized they generate call to MemberNameSpecialized to check if this member should be skipped.
        /// In this way we can skip empty collections (which cannot be achieved by DefaultValueAttribute).
        /// We can also add some additional checks.
        /// https://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlserializer.aspx?cs-lang=csharp#code-snippet-5
        /// </summary>
        [XmlIgnore]
        public bool DropsInternalSpecified
        {
            get { return DropsInternal.Count > 0; }
        }

        [XmlIgnore]
        internal NodeDeclarator Base { get; set; }

        #endregion Properties
    }

    [XmlType( "Drop" )]
    public class NodeDropDeclarator
    {
        /// <summary>
        /// Class of dropped data that will be assigned to object property described by Binding property.
        /// </summary>
        [XmlAttribute]
        public string Class { get; set; }

        /// <summary>
        /// Name of a property that is a member in class described by parent node of this declarator.
        /// </summary>
        [XmlAttribute]
        public string Binding { get; set; }
    }
}
